#include "StdAfx.h"
#include "CoordinationTaskCommunication.h"
#include "Communication/CommunicationManager.h"



void CoordinationTaskCommunication::RunningCommunication::OnCommunicationEvent(
	ICommunicationManager::ECommunicationEvent event, EntityId actorID, const CommPlayID& playID)
{
	switch (event)
	{
	case ICommunicationManager::CommunicationCancelled:
	case ICommunicationManager::CommunicationFinished:
	case ICommunicationManager::CommunicationExpired:
		{
			CoordinationActor actor(actorID);
			ActorStates::iterator it = actors.find(actor);
			//Only process events that match playID
			if(it != actors.end())
			{
					assert(it->second.playID == playID);
					ActorState& actorState = it->second;

					//Only drop the inactive count if this hasn't already finished
					if(actorState.state == CoordinationRunning)
						--activeCount;

					actorState.state = CoordinationFinished;
			}
		}
		break;
	default:
		break;
	}
}

CoordinationTaskCommunication::CoordinationTaskCommunication(const CoordinationSetup* setup, const CoordinationTaskMonitor* taskMonitor)
: m_timeout(0.0f)
, m_commID(0)
, m_channelID(0)
, m_expirity(0.0f)
, m_minSilence(-1.0f)
, m_isListener(true)
, m_ordering(SCommunicationRequest::Ordered)
, m_sync(setup, taskMonitor)
, m_setup(setup)
, m_taskMonitor(taskMonitor)
, m_ignoreSound(false)
, m_ignoreAnim(false)
{
}

CoordinationTaskCommunication::~CoordinationTaskCommunication()
{
	//Loop through all running commmuncations.
	for (RunningCommunications::iterator runningComIt = m_running.begin(), runningComItEnd = m_running.end()
		;runningComIt != runningComItEnd
		;++runningComIt)
	{
		ActorStates::iterator ait = runningComIt->second.actors.begin();
		ActorStates::iterator aend = runningComIt->second.actors.end();

		for ( ; ait != aend; ++ait)
		{
			ActorState& state = ait->second;
			if(state.playID)
			{
				gAIEnv.pCommunicationManager->RemoveInstanceListener(state.playID);
			}
		}
	}
}

const char* CoordinationTaskCommunication::GetName() const
{
	return "Comm";
}

const char* CoordinationTaskCommunication::GetDescription() const
{
	return gAIEnv.pCommunicationManager->GetCommunicationName(m_commID);
}

bool CoordinationTaskCommunication::LoadFromXML(int roleID, const XmlNodeRef& rootNode)
{
	const char* commName;
	if (!rootNode->getAttr("name", &commName))
	{
		AIWarning("Missing 'name' attribute for 'Communication' tag at line %d...", rootNode->getLine());

		return false;
	}

	m_commID = gAIEnv.pCommunicationManager->GetCommunicationID(commName);
	if (!m_commID)
	{
		AIWarning("Unknown communication name '%s' at line %d...", commName, rootNode->getLine());

		return false;
	}

	const char* channelName;
	if (!rootNode->getAttr("channel", &channelName))
	{
		AIWarning("Missing 'channel' attribute for 'Communication' tag at line %d...", rootNode->getLine());

		return false;
	}

	m_channelID = gAIEnv.pCommunicationManager->GetChannelID(channelName);
	if (!m_channelID)
	{
		AIWarning("Unknown channel name '%s' at line %d...", channelName, rootNode->getLine());

		return false;
	}

	if (rootNode->haveAttr("timeout"))
		rootNode->getAttr("timeout", m_timeout);

	if (rootNode->haveAttr("expirity"))
		rootNode->getAttr("expirity", m_expirity);

	if (rootNode->haveAttr("minSilence"))
		rootNode->getAttr("minSilence", m_minSilence);

	if (rootNode->haveAttr("listen"))
		rootNode->getAttr("listen", m_isListener);

	if (rootNode->haveAttr("ignoreSound"))
		rootNode->getAttr("ignoreSound", m_ignoreSound);

	if (rootNode->haveAttr("ignoreAnim"))
		rootNode->getAttr("ignoreAnim", m_ignoreAnim);


	if (rootNode->haveAttr("ordering"))
	{
		const char* ordering = 0;
		rootNode->getAttr("ordering", &ordering);

		if (!stricmp(ordering, "ordered"))
			m_ordering = SCommunicationRequest::Ordered;
		else if (!stricmp(ordering, "unordered"))
			m_ordering = SCommunicationRequest::Unordered;
		else
		{
			AIWarning("Invalid ordering '%s' for 'Communication' tag at line %d...", ordering, rootNode->getLine());

			return false;
		}
	}

	if (!m_sync.LoadFromXML(roleID, rootNode))
		return false;

	return true;
}

ECoordinationState CoordinationTaskCommunication::GetActorState(const CoordinationID& coordinationID,
																													 const CoordinationActor& actor) const
{
	RunningCommunications::const_iterator it = m_running.find(coordinationID);
	if (it != m_running.end())
	{
		const RunningCommunication& running = it->second;

		ActorStates::const_iterator ait = running.actors.find(actor);

		if (ait != running.actors.end())
			return ait->second.state;
	}

	return CoordinationNotRunning;
}

ECoordinationState CoordinationTaskCommunication::GetState(const CoordinationID& coordinationID) const
{
	RunningCommunications::const_iterator it = m_running.find(coordinationID);
	if (it != m_running.end())
	{
		const RunningCommunication& running = it->second;

		return running.state;
	}

	return CoordinationNotRunning;
}

bool CoordinationTaskCommunication::CanStart(const CoordinationID& coordinationID, const CoordinationActors& assignees) const
{
	return true;
}

void CoordinationTaskCommunication::Start(const CoordinationID& coordinationID,
																					const CoordinationActors& assignees)
{
	std::pair<RunningCommunications::iterator, bool> result = m_running.insert(
		RunningCommunications::value_type(coordinationID, RunningCommunication()));
	
	RunningCommunication& running = result.first->second;
	
	SCommunicationRequest request;
	request.channelID = m_channelID;
	request.commID = m_commID;
	request.contextExpirity = m_expirity;
	request.minSilence = m_minSilence;
	request.ordering = m_ordering;
	if(m_isListener)
		request.eventListener = &running;
	if(m_ignoreAnim)
		request.skipCommAnimation = true;
	if(m_ignoreSound)
		request.skipCommSound = true;

	CoordinationActors::const_iterator ait = assignees.begin();
	CoordinationActors::const_iterator aend = assignees.end();

	for ( ; ait != aend; ++ait)
	{
		request.actorID = ait->GetEntityID();
		request.configID = ait->GetCommunicationConfigID();

		ActorState& actorState = stl::map_insert_or_get(running.actors, *ait, ActorState());
		//If we don't listen for events, then assume communication is done
		actorState.state = CoordinationRunning;
		actorState.timeout = m_timeout;

		if (CommPlayID playID = gAIEnv.pCommunicationManager->PlayCommunication(request))
		{
			if(!m_isListener && !m_timeout)
				actorState.state = CoordinationFinished;
			else
				++running.activeCount;
			actorState.playID = playID;
		}
		else
			actorState.state = CoordinationFinished;
	}

	running.state = CoordinationRunning;
	if (result.second)
		m_sync.Start(coordinationID);
}

void CoordinationTaskCommunication::Stop(const CoordinationID& coordinationID)
{
	RunningCommunications::iterator it = m_running.find(coordinationID);
	assert(it != m_running.end());

	RunningCommunication& running = it->second;

	ActorStates::iterator ait = running.actors.begin();
	ActorStates::iterator aend = running.actors.end();

	for ( ; ait != aend; ++ait)
		Stop(coordinationID, ait->first); // TODO(marcio) fix the 2 extra-lookups

	m_running.erase(it);
	m_sync.Stop(coordinationID);
}

void CoordinationTaskCommunication::Stop(const CoordinationID& coordinationID, const CoordinationActor& actor)
{
	RunningCommunications::iterator it = m_running.find(coordinationID);
	assert(it != m_running.end());

	RunningCommunication& running = it->second;
	ActorStates::iterator ait = running.actors.find(actor);
	assert(ait != running.actors.end());

	ActorState& actorState = ait->second;

	if (actorState.state == CoordinationRunning)
	{
		gAIEnv.pCommunicationManager->StopCommunication(actorState.playID);
		--running.activeCount;
	}

	actorState.state = CoordinationFinished;
}

void CoordinationTaskCommunication::Update(float updateTime)
{
	if (!m_running.empty())
	{
		m_sync.Update();

		for (RunningCommunications::iterator runningComIt = m_running.begin(), runningComItEnd = m_running.end()
			;runningComIt != runningComItEnd
			;++runningComIt)
		{
			const CoordinationID& coordinationID = runningComIt->first;
			RunningCommunication& running = runningComIt->second;

			if (m_timeout > 0.0f)
			{
				for (ActorStates::iterator ait = running.actors.begin(), aend = running.actors.end()
					;ait != aend
					;++ait)
				{
					ActorState& actorState = ait->second;

					if (actorState.state == CoordinationRunning)
					{
						actorState.timeout -= updateTime;

						if (actorState.timeout <= 0.0f)
						{
							const CoordinationActor& actor = ait->first;

							gAIEnv.pCommunicationManager->StopCommunication(actorState.playID);
							actorState.state = CoordinationWaiting;
							--running.activeCount;
						}
					}
				}
			}

			if (running.activeCount < 1)
			{
				if (m_sync.GetState(coordinationID) == CoordinationFinished)
					running.state = CoordinationFinished;
				else
					running.state = CoordinationWaiting;
			}
		}
	}
}

void CoordinationTaskCommunication::Serialize(TSerialize ser)
{
	ser.Value("m_running", m_running);
	m_sync.Serialize(ser);
}